package marco.android.CouchControl;

import java.util.Vector;

import marco.android.CouchControl.TouchPadPanel.TouchPadToSurfaceListener;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Paint.Align;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;

public class CanvasThread extends Thread implements TouchPadToSurfaceListener
{
	private SurfaceHolder mSurfaceHolder;
	private int mCanvasH = 1;
	private int mCanvasW = 1;
	private Bitmap mBackground;
	private Paint mPaint;
	private Path mPath;
	private float mScaleFactor = 1.f;
	//private float mFocusX = 0, mFocusY = 0;
	private boolean mParticlesStop = true;
	private Vector<Particle> mParticles = new Vector<Particle>();
	//private int mMode;
	private float mX, mY;
	private static final float TOUCH_TOLERANCE = 1;
	private static final int PARTICLE_COUNT = 100;
	//private float[] mLine = new float[200];
	private int mSigBoxX, mSigBoxY, mSigBoxW, mSigBoxH, mTextureW, mTextureH;
	private float mDt = 1.f / 60.f;
	Vibrator mVibe = null;
	Bitmap mTexture = null;

	public CanvasThread(Context context, SurfaceHolder surfHolder)
	{
		mSurfaceHolder = surfHolder;
		mBackground = Bitmap.createBitmap(mCanvasW, mCanvasH, Bitmap.Config.ARGB_8888);
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setDither(true);
		mX = 0;
		mY = 0;
		mTexture = BitmapFactory.decodeResource(context.getResources(), R.drawable.bead_single_blue);
		mTextureW = mTexture.getWidth();
		mTextureH = mTexture.getHeight();
		mPath = new Path();
		mVibe = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
	}
	
	 /* Callback invoked when the surface dimensions change. */
    public void SurfaceChanged(int width, int height)
    {
        // synchronized to make sure these all change atomically
        synchronized (mSurfaceHolder)
        {
            mCanvasW = width;
            mCanvasH = height;

            // don't forget to resize the background image
            mBackground = Bitmap.createScaledBitmap(mBackground, width, height, true);
            UpdateSignatureBoxSize();
            
            mParticles.clear();
        }
    }
    
	private void UpdateSignatureBoxSize()
	{
		float w = mBackground.getWidth();
		float h = mBackground.getHeight();
		float deltaEdge = 0.0f;
		float deltaLR = deltaEdge * w;
		float deltaTB = deltaEdge * h;

		mSigBoxX = (int)deltaLR;
		mSigBoxY = (int)(deltaTB * 2.f);
		mSigBoxW = (int)(w - deltaLR);
		mSigBoxH = (int)(h - deltaTB);
	}
	
	public boolean isWithinBounds(float x, float y, float z)
	{
		boolean b = false;
		synchronized(mSurfaceHolder)
		{
		   b = (x <= mSigBoxW && x >= mSigBoxX &&
			    y <= mSigBoxH && y >= mSigBoxY);
		}
		return(b);
	}
	
	public void onScale(float scaleFactor, float focusX, float focusY)
	{
		mScaleFactor *= scaleFactor;

		// Don't let the object get too small or too large.
		mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));
//		mFocusX = focusX;
//		mFocusY = focusY;
	}
	
	public void Pause()
	{
//		synchronized (mSurfaceHolder)
//		{
//           if(mMode == STATE_RUNNING)
//           {
//              setState(STATE_PAUSE);
//           }
//        }
	}

	@Override
	public void run()
	{
		Canvas c = null;
		while(!isInterrupted())
		{
			c = null;
			try
			{
				c = mSurfaceHolder.lockCanvas();
				synchronized(mSurfaceHolder)
				{
					//c.drawBitmap(mBackground, 0, 0, mBitmapPaint);
					c.drawColor(Color.BLACK);
					c.save();
					//c.translate(mFocusX, mFocusY);
					c.scale(mScaleFactor, mScaleFactor);
					
					drawSignatureBoxAndLine(c);
					Fling();
					UpdateParticles(c);
					DrawPath(c);
					
					c.restore();
				}
			}
			finally
			{
				// Do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null)
                {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
			}
			
			try
			{
				sleep((int)(mDt * 1000.f));
			}
			catch(InterruptedException e)
			{
				break;
			}
		}
	}
	
	void UpdateParticles(Canvas c)
	{
		for(int i = 0; i < mParticles.size(); ++i)
		{
			if(mParticles.get(i).Update() == true)
			{
				// Particle expired, remove from surface.
				mParticles.remove(i);
				if(mParticlesStop == false)
				{
				   // Screen is still being touched, replace the expired particle.
					
					//mParticles.add(new PointerAnimation(x, y, 1.f/30.f));
					mParticles.add(new Particle(mX, mCanvasH - mY, 0, mCanvasH, 0, mCanvasW, mDt));
				}
			}
			else
			{
				Particle p = mParticles.get(i);
				// invert the Y position because the canvas origin
				// is at the top of the screen and the screen Y
				// axis is positive down but the particle frame Y
				// axis is positive up;
				c.drawPoint(p.GetX(), mCanvasH - p.GetY(), p.GetPaint());
//				int w = mTextureW / 20;
//				int h = mTextureH / 20;
//				int x = (int)(p.GetX() + (w / 2.f));
//				int y = (int)(p.GetY() - (h / 2.f));
//				Rect r = new Rect(x, y, x+w, y+h);
//				c.save();
//				c.rotate(p.GetBodyRotation(), p.GetX(), p.GetY());
//				c.drawBitmap(mTexture, null, r, p.GetPaint());
//				c.restore();
			}
		}
	}
	
	private void InitFingerStroke()
	{
		mPaint.setColor(0xFFFF0000);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeWidth(8);
	}
	
	private void DrawPath(Canvas c)
	{
		InitFingerStroke();
		//c.drawPath(mPath, mPaint);
		//c.drawPoints(mLine, 0, pointCnt, mPaint);
		//System.out.println("Drawing");
	}
	
	public void onDoubleTap(float x, float y)
	{
		onDown(x, y);
		//mScaleFactor = (mScaleFactor == 1.f ? 2.f : 1.f);
	}
	
	public void onDown(float x, float y)
	{
		mPath.reset();
		mPath.moveTo(x, y);
		mX = x;
		mY = y;
		mParticlesStop = false;

		int j = (mParticles.size() == 0 ? PARTICLE_COUNT : PARTICLE_COUNT - mParticles.size());
		for(int i = 0; i < j; ++i)
		{
			mParticles.add(new Particle(x, mCanvasH - y, 0, mCanvasH, 0, mCanvasW, mDt));
		}
	}

	public void onDrag(float dx, float dy)
	{
		if(Math.abs(dx) >= TOUCH_TOLERANCE || Math.abs(dy) >= TOUCH_TOLERANCE)
		{
			synchronized(mSurfaceHolder)
			{
			   mX += dx;
			   mY += dy;
			   if(mX > mSigBoxW || mX < mSigBoxX ||
				  mY > mSigBoxH || mY < mSigBoxY)
			   {
			      mVibe.vibrate(25);
			      mX = Math.max(Math.min(mX, mSigBoxW), mSigBoxX);
			      mY = Math.max(Math.min(mY, mSigBoxH), mSigBoxY);
			   }
			}

			System.out.println(String.valueOf(mX) + ", " + String.valueOf(mY));
		}
	}

	public void onUp(float x, float y)
	{
		mPath.lineTo(mX, mY);
		// commit the path to our offscreen
		// mCanvas.drawPath(mPath, mPaint);
		// kill this so we don't double draw
		mPath.reset();
		if(fling == false)
		{
		   // do not interrupt the particle
		   // animation until the fling gesture
		   // is complete.
		   mParticlesStop = true;
		}
	}

	private FlingHandler mFlinger = new FlingHandler();
	private boolean fling = false;
	public void onFling(float dx, float dy, long durationMilliSecs)
	{
		fling = mFlinger.onFling(dx, dy, durationMilliSecs);
	}
	
	private void Fling()
	{
		float [] pos = {0f, 0f};
		if(fling == true)
		{
			if(mFlinger.DoFling(pos) == true)
			{
				onDrag(pos[0], pos[1]);
			}
			else
			{
				mParticlesStop = true;
				fling = false;
			}
		}
	}
	
	public void onLongPress(float x, float y)
	{
	}
	
	private void drawSignatureBoxAndLine(Canvas c)
	{
		 float w = c.getWidth(); //mBitmap.getWidth();
		 float h = c.getHeight(); //mBitmap.getHeight();
		//
		// float deltaEdge = 0.05f;
		 //float deltaLR = deltaEdge * w;
		 //float deltaTB = deltaEdge * h;
		//
		 mPaint.setTextAlign(Align.LEFT);
		 mPaint.setStyle(Paint.Style.FILL);
		 mPaint.setTextSize(24.f);
		// mPaint.setStrokeWidth(1.0f);
		// mPaint.setColor(Color.GREEN);
		// String sign = "Please Sign Below Within Box";
		// Rect bounds = new Rect();
		// mPaint.getTextBounds(sign, 0, sign.length(), bounds);
		// float textX = (w - (bounds.right - bounds.left)) / 2.f;
		// mCanvas.drawText(sign, textX, deltaTB, mPaint);

		mPaint.setStrokeWidth(3);

		// mPaint.setColor(Color.BLUE);
		// Rect r = new Rect(0, 0, (int)w, (int)h);
		// mCanvas.drawRect(r, mPaint);

		mPaint.setStyle(Paint.Style.STROKE);
		if(fling == true)
		{
			mPaint.setColor(Color.MAGENTA);
		}
		else
		{
		   mPaint.setColor(Color.YELLOW);
		}
		// mCanvas.drawRect(mSigBoxX, mSigBoxY,
		// mSigBoxW, mSigBoxH,
		// mPaint);

		 Path box = new Path();
		 box.addRect(mSigBoxX, mSigBoxY, mSigBoxW, mSigBoxH,
		 Path.Direction.CCW);
		 c.drawPath(box, mPaint);

		// deltaEdge = deltaEdge * 1.5f;
		// deltaLR = deltaEdge * w;
		// deltaTB = deltaEdge * h;
		// mCanvas.drawLine(deltaLR, h - deltaTB,
		// w - deltaLR, h - deltaTB,
		// mPaint);

		 mPaint.setStrokeWidth(1.0f);
		 mPaint.setColor(Color.GREEN);
		 Rect bounds = new Rect();
		 String test = String.valueOf(mScaleFactor);// + ", " + String.valueOf(totalDx);
		 if(mParticles.size() > 0)
		 {
			 test += ", " + String.valueOf(mParticles.get(0).GetVx()) + ", " + String.valueOf(mParticles.get(0).GetVy());
		 }
		 mPaint.getTextBounds(test, 0, test.length(), bounds);
		 float textX = (w - (bounds.right - bounds.left)) / 2.f;
		 c.drawText(test, textX, h / 2.f, mPaint);
		 
		 mPaint.setStrokeWidth(20);
		 mPaint.setColor(Color.CYAN);
		 c.drawPoint(mX, mY, mPaint);
	}
}
